package codigo;

import java.util.List;

import visitor.DefaultVisitor;
import ast.DeclaFuncion;
import ast.DeclaVariable;
import ast.Declaracion;
import ast.Programa;
import ast.TipoFuncion;
import ast.TipoStruct;

public class GestionDeMemoria extends DefaultVisitor {

	private int direccionEstatica = 0;

	// class Programa { List<Declaracion> declaraciones; }
	public Object visit(Programa nodo, Object param) {
		super.visit(nodo, param);
		return null;
	}

	@Deprecated
	// class DeclaVariable { Expresion expresion; }
	public Object visit2(DeclaVariable nodo, Object offset) {

		if (offset == null) {
			nodo.direccionMemoria = direccionEstatica;
			System.out.println("Variable : '" + nodo.getNombre()
					+ "' en direccion " + nodo.direccionMemoria);
			if (nodo.getTipo() instanceof TipoStruct) {
				int offSet = 0;
				for (Declaracion child : ((TipoStruct) nodo.getTipo()).declaracionesStruct) {
					child.accept(this, offSet);
					offSet += child.getTipo().numeroBytes();
				}
			}
			direccionEstatica += nodo.getTipo().numeroBytes();

		} else {
			nodo.direccionMemoria = (int) offset;
			System.out.println("Campo : '" + nodo.getNombre() + "' con offset "
					+ nodo.direccionMemoria);
		}
		return null;
	}

	// class DeclaVariable { Expresion expresion; }
	public Object visit(DeclaVariable nodo, Object offset) {

		if (offset == null) {
			if (nodo.getTipo() instanceof TipoStruct
					&& ((TipoStruct) nodo.getTipo()).nombre.equals(nodo
							.getNombre())) {
				int offSet = 0;
				for (Declaracion child : ((TipoStruct) nodo.getTipo()).declaracionesStruct) {
					child.accept(this, offSet);
					offSet += child.getTipo().numeroBytes();
				}
			} else {
				nodo.direccionMemoria = direccionEstatica;
				System.out.println("Variable : '" + nodo.getNombre()
						+ "' en direccion " + nodo.direccionMemoria);
				direccionEstatica += nodo.getTipo().numeroBytes();
			}
		} else {
			nodo.direccionMemoria = (int) offset;
			System.out.println("Campo : '" + nodo.getNombre() + "' con offset "
					+ nodo.direccionMemoria);
		}
		return null;
	}

	// class DeclaFuncion { List<Sentencia> sentenciasOpt; }
	public Object visit(DeclaFuncion nodo, Object param) {
		// Se almacenan el bp y la dir de retorno (en direcciones superiores van
		// los parametros y en inferiores las locales).
		int offsetPila = 4;
		List<DeclaVariable> parametros = ((TipoFuncion) nodo.getTipo()).variables;
		for (int i = parametros.size() - 1; i >= 0; i--) {
			parametros.get(i).direccionMemoria = offsetPila;
			System.out.println("Funcion : Direccion de parametro '"
					+ parametros.get(i).getNombre() + "' en direccion BP + "
					+ offsetPila);
			if (parametros.get(i).getTipo().isPrimitivo())
				offsetPila += parametros.get(i).getTipo().numeroBytes();
			else
				offsetPila +=2;
		}
		offsetPila -= 4;
		if (parametros.size() == 0)
			offsetPila = 0;
		((TipoFuncion) nodo.getTipo()).offsetParametros = offsetPila;
		// Asignacion de variables locales
		offsetPila = 0;

		for (DeclaVariable local : nodo.variablesLocales) {
			offsetPila -= local.getTipo().numeroBytes();
			local.direccionMemoria = offsetPila;
			System.out.println("Variable local: Direccion de local '"
					+ local.getNombre() + "' en BP " + offsetPila);
		}
		nodo.offsetVariables = Math.abs(offsetPila);
		return null;
	}
}
